package gui;

import gui.settings.SettingsController;
import client.ConnectionStatus;
import etc.SynchronizationParameters;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Observable;
import java.util.Observer;
import javax.swing.JOptionPane;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.table.DefaultTableModel;
import server.SyncServer;

public class MainFrame extends javax.swing.JFrame implements Observer {

	private static ClientController clientController = new ClientController();
	
	private static ServerController serverController = new ServerController();

	private static MainFrame mf = null;

	public static ClientController getClientController() {
		return clientController;
	}

	private Boolean syncInProgress = false;

    /** Creates new form MainFrame */
    public MainFrame() {
        initComponents();
		setLocationRelativeTo(null);
    }

    /** This method is called from within the constructor to
     * initialize the form.
     * WARNING: Do NOT modify this code. The content of this method is
     * always regenerated by the Form Editor.
     */
    @SuppressWarnings("unchecked")
    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
    private void initComponents() {
        java.awt.GridBagConstraints gridBagConstraints;

        synchronizeButton = new javax.swing.JButton();
        abortButtton = new javax.swing.JButton();
        statusProgressbar = new javax.swing.JProgressBar();
        clientStatusLabel = new javax.swing.JLabel();
        clientStatusTextField = new javax.swing.JTextField();
        tableSplitPane = new javax.swing.JSplitPane();
        remoteFilesTableScrollPane = new javax.swing.JScrollPane();
        remoteFilesTable = new javax.swing.JTable();
        localFilesTableScrollPane = new javax.swing.JScrollPane();
        localFilesTable = new javax.swing.JTable();
        serverStatusLabel = new javax.swing.JLabel();
        serverStatusTextField = new javax.swing.JTextField();
        mainMenuBar = new javax.swing.JMenuBar();
        fileMenu = new javax.swing.JMenu();
        connectMenuItem = new javax.swing.JMenuItem();
        disconnectMenuItem = new javax.swing.JMenuItem();
        fileMenuConnectionSeperator = new javax.swing.JPopupMenu.Separator();
        refreshFileListMenuItem = new javax.swing.JMenuItem();
        synchronizeMenuItem = new javax.swing.JMenuItem();
        fileMenuSynchronizationSeperator = new javax.swing.JPopupMenu.Separator();
        exitMenuItem = new javax.swing.JMenuItem();
        advancedMenu = new javax.swing.JMenu();
        settingsMenuItem = new javax.swing.JMenuItem();

        setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
        setTitle("JSimpleSync - CSE471 Term Project - Spring 2011 - Cihat Keser");
        getContentPane().setLayout(new java.awt.GridBagLayout());

        synchronizeButton.setFont(new java.awt.Font("Tahoma", 1, 12));
        synchronizeButton.setText("Synchronize");
        synchronizeButton.setEnabled(false);
        synchronizeButton.setPreferredSize(new java.awt.Dimension(110, 30));
        synchronizeButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                synchronizeButtonActionPerformed(evt);
            }
        });
        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
        gridBagConstraints.weighty = 0.1;
        gridBagConstraints.insets = new java.awt.Insets(10, 15, 5, 5);
        getContentPane().add(synchronizeButton, gridBagConstraints);

        abortButtton.setFont(new java.awt.Font("Tahoma", 1, 12));
        abortButtton.setText("Abort");
        abortButtton.setEnabled(false);
        abortButtton.setPreferredSize(new java.awt.Dimension(110, 30));
        abortButtton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                abortButttonActionPerformed(evt);
            }
        });
        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.anchor = java.awt.GridBagConstraints.EAST;
        gridBagConstraints.weighty = 0.1;
        gridBagConstraints.insets = new java.awt.Insets(10, 5, 5, 15);
        getContentPane().add(abortButtton, gridBagConstraints);

        statusProgressbar.setPreferredSize(new java.awt.Dimension(250, 20));
        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 0;
        gridBagConstraints.gridy = 3;
        gridBagConstraints.gridwidth = 2;
        gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
        gridBagConstraints.weightx = 1.0;
        gridBagConstraints.weighty = 0.1;
        gridBagConstraints.insets = new java.awt.Insets(5, 15, 5, 15);
        getContentPane().add(statusProgressbar, gridBagConstraints);

        clientStatusLabel.setText("Client Status:");
        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 0;
        gridBagConstraints.gridy = 2;
        gridBagConstraints.weighty = 0.1;
        gridBagConstraints.insets = new java.awt.Insets(5, 15, 5, 5);
        getContentPane().add(clientStatusLabel, gridBagConstraints);

        clientStatusTextField.setEditable(false);
        clientStatusTextField.setText("N/A");
        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 1;
        gridBagConstraints.gridy = 2;
        gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
        gridBagConstraints.weighty = 0.1;
        gridBagConstraints.insets = new java.awt.Insets(5, 5, 5, 15);
        getContentPane().add(clientStatusTextField, gridBagConstraints);

        remoteFilesTable.setModel(new javax.swing.table.DefaultTableModel(
            new Object [][] {

            },
            new String [] {
                "File", "Status", "Size (Bytes)", "Last Modified"
            }
        ) {
            Class[] types = new Class [] {
                java.lang.String.class, java.lang.String.class, java.lang.String.class, java.lang.String.class
            };
            boolean[] canEdit = new boolean [] {
                false, false, false, false
            };

            public Class getColumnClass(int columnIndex) {
                return types [columnIndex];
            }

            public boolean isCellEditable(int rowIndex, int columnIndex) {
                return canEdit [columnIndex];
            }
        });
        remoteFilesTableScrollPane.setViewportView(remoteFilesTable);
        remoteFilesTable.getColumnModel().getColumn(0).setCellRenderer(new TableColoredRenderer());
        remoteFilesTable.getColumnModel().getColumn(1).setMinWidth(5);
        remoteFilesTable.getColumnModel().getColumn(1).setPreferredWidth(10);
        remoteFilesTable.getColumnModel().getColumn(1).setCellRenderer(new TableColoredRenderer());
        remoteFilesTable.getColumnModel().getColumn(2).setMinWidth(5);
        remoteFilesTable.getColumnModel().getColumn(2).setPreferredWidth(10);
        remoteFilesTable.getColumnModel().getColumn(2).setCellRenderer(new TableColoredRenderer());
        remoteFilesTable.getColumnModel().getColumn(3).setCellRenderer(new TableColoredRenderer());

        tableSplitPane.setRightComponent(remoteFilesTableScrollPane);

        localFilesTable.setModel(new javax.swing.table.DefaultTableModel(
            new Object [][] {

            },
            new String [] {
                "File", "Status", "Size (Bytes)", "Last Modified"
            }
        ) {
            Class[] types = new Class [] {
                java.lang.String.class, java.lang.String.class, java.lang.String.class, java.lang.String.class
            };
            boolean[] canEdit = new boolean [] {
                false, false, false, false
            };

            public Class getColumnClass(int columnIndex) {
                return types [columnIndex];
            }

            public boolean isCellEditable(int rowIndex, int columnIndex) {
                return canEdit [columnIndex];
            }
        });
        localFilesTableScrollPane.setViewportView(localFilesTable);
        localFilesTable.getColumnModel().getColumn(0).setCellRenderer(new TableColoredRenderer());
        localFilesTable.getColumnModel().getColumn(1).setMinWidth(5);
        localFilesTable.getColumnModel().getColumn(1).setPreferredWidth(10);
        localFilesTable.getColumnModel().getColumn(1).setCellRenderer(new TableColoredRenderer());
        localFilesTable.getColumnModel().getColumn(2).setMinWidth(5);
        localFilesTable.getColumnModel().getColumn(2).setPreferredWidth(10);
        localFilesTable.getColumnModel().getColumn(2).setCellRenderer(new TableColoredRenderer());
        localFilesTable.getColumnModel().getColumn(3).setCellRenderer(new TableColoredRenderer());

        tableSplitPane.setLeftComponent(localFilesTableScrollPane);

        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 0;
        gridBagConstraints.gridy = 4;
        gridBagConstraints.gridwidth = 2;
        gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
        gridBagConstraints.weightx = 1.0;
        gridBagConstraints.weighty = 0.7;
        gridBagConstraints.insets = new java.awt.Insets(5, 15, 10, 15);
        getContentPane().add(tableSplitPane, gridBagConstraints);

        serverStatusLabel.setText("Server Status:");
        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 0;
        gridBagConstraints.gridy = 1;
        gridBagConstraints.weighty = 0.1;
        gridBagConstraints.insets = new java.awt.Insets(5, 15, 5, 5);
        getContentPane().add(serverStatusLabel, gridBagConstraints);

        serverStatusTextField.setEditable(false);
        serverStatusTextField.setText("N/A");
        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 1;
        gridBagConstraints.gridy = 1;
        gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
        gridBagConstraints.weighty = 0.1;
        gridBagConstraints.insets = new java.awt.Insets(5, 5, 5, 15);
        getContentPane().add(serverStatusTextField, gridBagConstraints);

        fileMenu.setMnemonic('f');
        fileMenu.setText("File");

        connectMenuItem.setAccelerator(javax.swing.KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_C, java.awt.event.InputEvent.CTRL_MASK));
        connectMenuItem.setMnemonic('c');
        connectMenuItem.setText("Connect");
        connectMenuItem.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                connectMenuItemActionPerformed(evt);
            }
        });
        fileMenu.add(connectMenuItem);

        disconnectMenuItem.setAccelerator(javax.swing.KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_D, java.awt.event.InputEvent.CTRL_MASK));
        disconnectMenuItem.setMnemonic('d');
        disconnectMenuItem.setText("Disconnect");
        disconnectMenuItem.setEnabled(false);
        disconnectMenuItem.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                disconnectMenuItemActionPerformed(evt);
            }
        });
        fileMenu.add(disconnectMenuItem);
        fileMenu.add(fileMenuConnectionSeperator);

        refreshFileListMenuItem.setAccelerator(javax.swing.KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_R, java.awt.event.InputEvent.CTRL_MASK));
        refreshFileListMenuItem.setText("Refresh File Lists");
        refreshFileListMenuItem.setEnabled(false);
        refreshFileListMenuItem.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                refreshFileListMenuItemActionPerformed(evt);
            }
        });
        fileMenu.add(refreshFileListMenuItem);

        synchronizeMenuItem.setAccelerator(javax.swing.KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_S, java.awt.event.InputEvent.CTRL_MASK));
        synchronizeMenuItem.setMnemonic('s');
        synchronizeMenuItem.setText("Synchronize");
        synchronizeMenuItem.setEnabled(false);
        synchronizeMenuItem.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                synchronizeMenuItemActionPerformed(evt);
            }
        });
        fileMenu.add(synchronizeMenuItem);
        fileMenu.add(fileMenuSynchronizationSeperator);

        exitMenuItem.setAccelerator(javax.swing.KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_E, java.awt.event.InputEvent.CTRL_MASK));
        exitMenuItem.setMnemonic('e');
        exitMenuItem.setText("Exit");
        exitMenuItem.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                exitMenuItemActionPerformed(evt);
            }
        });
        fileMenu.add(exitMenuItem);

        mainMenuBar.add(fileMenu);

        advancedMenu.setMnemonic('a');
        advancedMenu.setText("Advanced");

        settingsMenuItem.setAccelerator(javax.swing.KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_S, java.awt.event.InputEvent.ALT_MASK | java.awt.event.InputEvent.CTRL_MASK));
        settingsMenuItem.setMnemonic('s');
        settingsMenuItem.setText("Settings");
        settingsMenuItem.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                settingsMenuItemMouseClicked(evt);
            }
        });
        advancedMenu.add(settingsMenuItem);

        mainMenuBar.add(advancedMenu);

        setJMenuBar(mainMenuBar);

        pack();
    }// </editor-fold>//GEN-END:initComponents

	private void settingsMenuItemMouseClicked(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_settingsMenuItemMouseClicked
		serverController.stopServer();
		getClientController().stopClient();
		updateGuiAccordingToConnStatus(ConnectionStatus.DISCONNECTED);

		HashMap<String, Object> changedSettings = SettingsController.showSettingsDialog( SynchronizationParameters.getAsHashMap() );
		if( changedSettings != null ) {
			SynchronizationParameters.loadFromHashMap( changedSettings );
			SynchronizationParameters.saveToPropertiesFile();
		}

		serverController.startServer(mf);
	}//GEN-LAST:event_settingsMenuItemMouseClicked

	private void connectMenuItemActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_connectMenuItemActionPerformed
		ConnectionStatus status = getClientController().startClient();
		updateGuiAccordingToConnStatus(status);
	}//GEN-LAST:event_connectMenuItemActionPerformed

	private void exitMenuItemActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_exitMenuItemActionPerformed
		if( getSyncInProgress() ) {
			int dialogResult =
			JOptionPane.showConfirmDialog(
				null,
				"Exiting now will stop the synchronization.\nDo you still want to exit?",
				"Warning",
				JOptionPane.YES_NO_OPTION,
				JOptionPane.WARNING_MESSAGE
			);

			if( dialogResult == JOptionPane.YES_OPTION ) {
				getClientController().stopClient();
				serverController.stopServer();
			}
		}else{
			getClientController().stopClient();
			serverController.stopServer();
		}
		
		System.exit(0);
	}//GEN-LAST:event_exitMenuItemActionPerformed

	private void disconnectMenuItemActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_disconnectMenuItemActionPerformed
		if( getSyncInProgress() ) {
			int dialogResult =
			JOptionPane.showConfirmDialog(
				null,
				"Disconnecting now will stop the synchronization.\nDo you still want to disconnect?",
				"Warning",
				JOptionPane.YES_NO_OPTION,
				JOptionPane.WARNING_MESSAGE
			);

			if( dialogResult == JOptionPane.YES_OPTION ) {
				getClientController().stopClient();
				setSyncInProgress((Boolean) false);
			}
		}else{
			getClientController().stopClient();
		}

		updateGuiAccordingToConnStatus(ConnectionStatus.DISCONNECTED);
	}//GEN-LAST:event_disconnectMenuItemActionPerformed

	private void synchronizeButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_synchronizeButtonActionPerformed
		String[] options = { "Mirror remote directory on local directory.", "Mirror local directory on remote directory."};
		String selection = (String) JOptionPane.showInputDialog(
				null,
				"Please choose the synchronization direction:",
				"Synchronization Direction",
				JOptionPane.INFORMATION_MESSAGE,
				null,
				options,
				null);

		if( selection != null ) {
			setSyncInProgress((Boolean) true);
			updateGuiAccordingToConnStatus(ConnectionStatus.SYNCING);

			if( selection.equalsIgnoreCase(options[0]) ) {
				getClientController().mirrorRemoteOnLocal();
			}else{
				getClientController().mirrorLocalOnRemote();
			}
		}
	}//GEN-LAST:event_synchronizeButtonActionPerformed

	private void synchronizeMenuItemActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_synchronizeMenuItemActionPerformed
		synchronizeButtonActionPerformed(evt);
	}//GEN-LAST:event_synchronizeMenuItemActionPerformed

	private void refreshFileListMenuItemActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_refreshFileListMenuItemActionPerformed
		if( !getSyncInProgress() && (clientController != null) ) {
			clientController.getAndCompareFiles();
		}
	}//GEN-LAST:event_refreshFileListMenuItemActionPerformed

	private void abortButttonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_abortButttonActionPerformed
		getClientController().stopClient();
		updateGuiAccordingToConnStatus(ConnectionStatus.DISCONNECTED);
		setSyncInProgress(false);
	}//GEN-LAST:event_abortButttonActionPerformed

    /**
    * @param args the command line arguments
    */
    public static void main(String args[]) {
		try {
			UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
		} catch (Exception ex) {}

        java.awt.EventQueue.invokeLater(new Runnable() {
            public void run() {
				SynchronizationParameters.loadFromPropertiesFile();
				
                mf = new MainFrame();
				mf.setVisible(true);
				
				serverController.startServer(mf);
				getClientController().addObserver(mf);
            }
        });
    }

    // Variables declaration - do not modify//GEN-BEGIN:variables
    private javax.swing.JButton abortButtton;
    private javax.swing.JMenu advancedMenu;
    private javax.swing.JLabel clientStatusLabel;
    private javax.swing.JTextField clientStatusTextField;
    private javax.swing.JMenuItem connectMenuItem;
    private javax.swing.JMenuItem disconnectMenuItem;
    private javax.swing.JMenuItem exitMenuItem;
    private javax.swing.JMenu fileMenu;
    private javax.swing.JPopupMenu.Separator fileMenuConnectionSeperator;
    private javax.swing.JPopupMenu.Separator fileMenuSynchronizationSeperator;
    private javax.swing.JTable localFilesTable;
    private javax.swing.JScrollPane localFilesTableScrollPane;
    private javax.swing.JMenuBar mainMenuBar;
    private javax.swing.JMenuItem refreshFileListMenuItem;
    private javax.swing.JTable remoteFilesTable;
    private javax.swing.JScrollPane remoteFilesTableScrollPane;
    private javax.swing.JLabel serverStatusLabel;
    private javax.swing.JTextField serverStatusTextField;
    private javax.swing.JMenuItem settingsMenuItem;
    private javax.swing.JProgressBar statusProgressbar;
    private javax.swing.JButton synchronizeButton;
    private javax.swing.JMenuItem synchronizeMenuItem;
    private javax.swing.JSplitPane tableSplitPane;
    // End of variables declaration//GEN-END:variables
	
	public void update(Observable o, Object arg) {
		if( o instanceof SyncServer ) {
			if( arg instanceof String ) {
				String string = (String) arg;
				SwingUtilities.invokeLater( new TextUpdater( string, serverStatusTextField ) );
			}
		}else if( o instanceof  ClientController ) {
			if( arg instanceof String ) {
				String string = (String) arg;

				if( string.equalsIgnoreCase("SYNC_FINISHED") ) {
					SwingUtilities.invokeLater( new SyncFinishedGuiUpdater( mf ) );
				}else{
					SwingUtilities.invokeLater( new TextUpdater( string, clientStatusTextField ) );
				}
			}else if( arg instanceof ArrayList ) {
				ArrayList<String[]> fileList = (ArrayList<String[]>) arg;

				String side = (String) fileList.get(0)[0];
				fileList.remove(0);

				DefaultTableModel dm = null;
				if( side.equalsIgnoreCase("local") ) {
					dm = (DefaultTableModel)localFilesTable.getModel();

				}else if( side.equalsIgnoreCase("remote") ) {
					dm = (DefaultTableModel)remoteFilesTable.getModel();
				}

				if( dm != null ) {
					if( fileList.size() > 1 ) {
						for (String[] row : fileList) {
							SwingUtilities.invokeLater( new TableAppender(dm,  row ) );
						}
					}else{
						SwingUtilities.invokeLater( new TableCleaner(dm) );
					}
				}
			}else if( arg instanceof Integer ) {
				Integer integer = (Integer) arg;
				SwingUtilities.invokeLater( new ProgressUpdater(statusProgressbar, integer) );
			}
		}
	}

	public void updateGuiAccordingToConnStatus( ConnectionStatus status ) {
		if( status != null ) {
			connectMenuItem.setEnabled(status == ConnectionStatus.DISCONNECTED);
			disconnectMenuItem.setEnabled(status != ConnectionStatus.DISCONNECTED);
			synchronizeMenuItem.setEnabled(status == ConnectionStatus.AUTHORIZED);
			refreshFileListMenuItem.setEnabled(status == ConnectionStatus.AUTHORIZED);
			synchronizeButton.setEnabled(status == ConnectionStatus.AUTHORIZED);
			abortButtton.setEnabled(status == ConnectionStatus.SYNCING);
			settingsMenuItem.setEnabled(status != ConnectionStatus.SYNCING);

			if( status != ConnectionStatus.SYNCING ) {
				statusProgressbar.setValue( 0 );
				statusProgressbar.repaint();
			}
		}
	}

	public Boolean getSyncInProgress() {
		return syncInProgress;
	}

	public void setSyncInProgress(Boolean syncInProgress) {
		this.syncInProgress = syncInProgress;
	}

}
